<?php
// 应用公共文件
use think\Response;
use think\facade\Env;
use think\facade\Log;
use think\facade\Config;
use think\facade\Request;
use app\common\exception\BaseException;
use app\common\exception\DebugException;
use think\exception\HttpResponseException;

/**
 * 当前是否为调试模式
 * @return bool
 */
function is_debug(): bool
{
    return (bool)Env::instance()->get('APP_DEBUG');
}

/**
 * 结束执行
 */
function app_end()
{
    if (\request()->isCli()) {
        exit(PHP_EOL);
    }
    throw new HttpResponseException(Response::create());
}
/**
 * 输出错误信息
 * @param string $message 报错信息
 * @param int|null $status 状态码,默认为配置文件status.error
 * @param array $data 附加数据
 * @throws BaseException
 */
function throwError(string $message, ?int $status = null, array $data = [])
{
    is_null($status) && $status = config('status.error');
    throw new BaseException(['status' => $status, 'message' => $message, 'data' => $data]);
}
/**
 * 写入日志 (使用tp自带驱动记录到runtime目录中)
 * @param mixed $value
 * @param string $type
 */
function log_set($value, string $type = 'info')
{
    $content = is_string($value) ? $value : print_r($value, true);
    Log::record($content, $type);
}
/**
 * 打印调试函数 html
 * @param $content
 * @param bool $export
 */
function pre($content, bool $export = false)
{
    $output = $export ? var_export($content, true) : print_r($content, true);
    echo "<pre>{$output}</pre>";
    app_end();
}

/**
 * 打印调试函数 json
 * @param $content
 * @param bool $export
 * @throws DebugException
 */
function pree($content, bool $export = false)
{
    $output = $export ? var_export($content, true) : $content;
    throw new DebugException([], $output);
}
/**
 * 获取当前的应用名称
 * @return mixed
 */
function app_name()
{
    return app('http')->getName();
}

// 校正uri地址
function formatUrl($url) {
    // 如果 URL 为空，返回一个默认 URL 或错误提示
    if (empty($url)) {
        return $url;
    }
    // 对 URL 进行初步过滤：去除两端的空格
    $url = trim($url);
    // 如果 URL 没有协议头，自动添加 http://
    if (!preg_match('#^https?://#', $url)) {
        $url = 'http://' . $url;
    }
    // 正则匹配并解析协议、域名和路径等部分
    if (preg_match('#^(https?://)([^/]+)(/.*)?$#', $url, $matches)) {
        $protocol = $matches[1]; // http:// 或 https://
        $domain = $matches[2];   // 域名部分
        // 处理路径部分
        $path = isset($matches[3]) ? preg_replace('#/+#', '/', $matches[3]) : '';
        // 确保路径没有以 / 结尾，除非是根路径
        $path = ($path !== '/' && $path !== '') ? rtrim($path, '/') : $path;
        // 拼接最终清理后的 URL
        return $protocol . $domain . $path;
    }
    // 如果正则匹配失败，返回源地址
    return $url;
}

/**
 * 获取当前url的子目录路径
 * @return string
 */
function root_url(): string
{
    static $rootUrl = '';
    if (empty($rootUrl)) {
        $request = Request::instance();
        $subUrl = str_replace('\\', '/', dirname($request->baseFile()));
        $rootUrl = $subUrl . ($subUrl === '/' ? '' : '/');
    }
    return $rootUrl;
}
/**
 * 获取当前域名及根路径
 * @return string
 */
function base_url(): string
{
    static $baseUrl = '';
    if (empty($baseUrl)) {
        $request = Request::instance();
        // url协议，设置强制https或自动获取
        $scheme = Config::get('route')['url_force_https'] ? 'https' : $request->scheme();
        // url子目录
        $rootUrl = root_url();
        // 拼接完整url
        $baseUrl = "{$scheme}://" . $request->host() . $rootUrl;
    }
    return $baseUrl;
}
/**
 * 下划线转驼峰
 * @param string $uncamelized_words
 * @param string $separator
 * @return string
 */
function camelize(string $uncamelized_words, string $separator = '_'): string
{
    $uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words));
    return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator);
}
/**
 * 驼峰转下划线
 * @param string $camelCaps
 * @param string $separator
 * @return string
 */
function uncamelize(string $camelCaps, string $separator = '_'): string
{
    return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
}
/**
 * 隐藏敏感字符
 * @param $value
 * @return string
 */
function substr_cut($value): string
{
    $strlen = mb_strlen($value, 'utf-8');
    if ($strlen <= 1) return $value;
    $firstStr = mb_substr($value, 0, 1, 'utf-8');
    $lastStr = mb_substr($value, -1, 1, 'utf-8');
    return $strlen == 2 ? $firstStr . str_repeat('*', $strlen - 1) : $firstStr . str_repeat("*", $strlen - 2) . $lastStr;
}
/**
 * 重写htmlspecialchars方法 (解决int类型过滤后后变为string类型)
 * @param $string
 * @return string|void
 */
function my_htmlspecialchars($string)
{
    return is_string($string) ? htmlspecialchars($string, ENT_COMPAT) : $string;
}
/**
 * 过滤emoji表情
 * @param $text
 * @return null|string|string[]
 */
function filter_emoji($text)
{
    if (!is_string($text)) {
        return $text;
    }
    // 此处的preg_replace用于过滤emoji表情
    // 如需支持emoji表情, 需将mysql的编码改为utf8mb4
    return preg_replace('/[\xf0-\xf7].{3}/', '', $text);
}
/**
 * 根据指定长度截取字符串
 * @param $str
 * @param int $length
 * @return bool|string
 */
function str_substr($str, int $length = 30)
{
    if (strlen($str) > $length) {
        $str = mb_substr($str, 0, $length);
    }
    return $str;
}
/**
 * 隐藏手机号中间四位 13012345678 -> 130****5678
 * @param string $mobile 手机号
 * @return string
 */
function hide_mobile(string $mobile): string
{
    return substr_replace($mobile, '****', 3, 4);
}
/**
 * 文本左斜杠转换为右斜杠
 * @param string $string
 * @return string
 */
function convert_left_slash(string $string): string
{
    return str_replace('\\', '/', $string);
}
/**
 * 多维数组合并
 * @param $array1
 * @param $array2
 * @return array
 */
function array_merge_multiple($array1, $array2): array
{
    $merge = $array1 + $array2;
    $data = [];
    foreach ($merge as $key => $val) {
        if (
            isset($array1[$key])
            && is_array($array1[$key])
            && isset($array2[$key])
            && is_array($array2[$key])
        ) {
            $data[$key] = is_assoc($array1[$key]) ? array_merge_multiple($array1[$key], $array2[$key]) : $array2[$key];
        } else {
            $data[$key] = $array2[$key] ?? $array1[$key];
        }
    }
    return $data;
}
/**
 * 判断是否为自定义索引数组
 * @param array $array
 * @return bool
 */
function is_assoc(array $array): bool
{
    if (empty($array)) return false;
    return array_keys($array) !== range(0, count($array) - 1);
}
/**
 * 获取全局唯一标识符
 * @param bool $trim
 * @return string
 */
function get_guid_v4(bool $trim = true): string
{
    // Windows
    if (function_exists('com_create_guid') === true) {
        $charid = com_create_guid();
        return $trim == true ? trim($charid, '{}') : $charid;
    }
    // OSX/Linux
    if (function_exists('openssl_random_pseudo_bytes') === true) {
        $data = openssl_random_pseudo_bytes(16);
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40);    // set version to 0100
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80);    // set bits 6-7 to 10
        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
    }
    // Fallback (PHP 4.2+)
    mt_srand(intval((double)microtime() * 10000));
    $charid = strtolower(md5(uniqid((string)rand(), true)));
    $hyphen = chr(45);                  // "-"
    $lbrace = $trim ? "" : chr(123);    // "{"
    $rbrace = $trim ? "" : chr(125);    // "}"
    return $lbrace .
        substr($charid, 0, 8) . $hyphen .
        substr($charid, 8, 4) . $hyphen .
        substr($charid, 12, 4) . $hyphen .
        substr($charid, 16, 4) . $hyphen .
        substr($charid, 20, 12) .
        $rbrace;
}

/**
 * 获取数组中指定的列
 * @param $source
 * @param $column
 * @return array
 */
function getArrayColumn($source, $column): array
{
    $columnArr = [];
    foreach ($source as $item) {
        isset($item[$column]) && $columnArr[] = $item[$column];
    }
    return $columnArr;
}

/**
 * 获取数组中指定的列 [支持多列]
 * @param $source
 * @param $columns
 * @return array
 */
function getArrayColumns($source, $columns): array
{
    $columnArr = [];
    foreach ($source as $item) {
        $temp = [];
        foreach ($columns as $index) {
            $temp[$index] = $item[$index];
        }
        $columnArr[] = $temp;
    }
    return $columnArr;
}

/**
 * 获取二维数组中指定字段的和
 * @param $array
 * @param $column
 * @return float|int
 */
function getArrayColumnSum($array, $column)
{
    $sum = 0;
    foreach ($array as $item) {
        $sum = my_bcadd($sum, $item[$column]);
    }
    return $sum;
}
/**
 * [bcsub 数字精度计算 相加]
 */
function my_bcadd($leftOperand, $rightOperand, $scale = 2)
{
    // bcadd(被加数,加数,保留几位小数);
    // bcadd(1,3,2);//4.00
    return \bcadd($leftOperand, $rightOperand, $scale);
}
/**
 * [bcsub 数字精度计算 相减]
 */
function my_bcsub($leftOperand, $rightOperand, $scale = 2)
{
    // bcsub(被减数,减数,保留几位小数)
    return \bcsub($leftOperand, $rightOperand, $scale);
}

/**
 * [bcsub 数字精度计算 相乘]
 */
function my_bcmul($leftOperand, $rightOperand, $scale = 2)
{
    // bcmul(被乘数,乘数,保留几位小数)
    return \bcmul($leftOperand, $rightOperand, $scale);
}
/**
 * [bcsub 数字精度计算 相除]
 */
function my_bcdiv($leftOperand, $rightOperand, $scale = 2)
{
    // bcdiv(被除数,除数,保留几位小数)
    return \bcdiv($leftOperand, $rightOperand, $scale);
}
/**
 * [bcsub 数字精度计算 比较大小]
 *【0表示 相同】 【1 表示 num1大】 【-1 表示 num2 大 或 其他】
 */
function my_bccomp($leftOperand, $rightOperand, $scale = 2): int
{
    // bccomp(值1,值2,比较的小数位数)
    return \bccomp($leftOperand, $rightOperand, $scale);
}

/**
 * 比较两个数值是否相等
 * @param $leftOperand
 * @param $rightOperand
 * @param int $scale
 * @return bool
 */
function my_bcequal($leftOperand, $rightOperand, int $scale = 2): bool
{
    return my_bccomp($leftOperand, $rightOperand, $scale) === 0;
}

/**
 * 数组转为json
 * @param $data
 * @param int $options
 * @return false|string
 */
function jsonEncode($data, int $options = JSON_UNESCAPED_UNICODE)
{
    return json_encode($data, $options);
}

/**
 * json转义为数组
 * @param $json
 * @return array|mixed
 */
function jsonDecode($json)
{
    return json_decode($json, true);
}

/**
 * 将xml数据转换为array
 * @param string $xml
 * @return array|mixed
 */
function xmlToArray(string $xml)
{
    // 禁止引用外部xml实体
    if (PHP_VERSION_ID < 80000) {
        libxml_disable_entity_loader(true);
    }
    return jsonDecode(jsonEncode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)));
}
/**
 * 设置默认的检索数据
 * @param array $query
 * @param array $default
 * @return array
 */
function setQueryDefaultValue(array $query, array $default = []): array
{
    $data = array_merge($default, $query);
    foreach ($query as $field => $value) {
        // 不存在默认值跳出循环
        if (!isset($default[$field])) continue;
        // 如果传参为空, 设置默认值
        if (empty($value) && $value !== '0') {
            $data[$field] = $default[$field];
        }
    }
    return $data;
}